#include <cstring>
#include <cstdlib>
#include <vector>
#include <unordered_set>

#include "echo_arch.h"
#include "arch_types.h"
#include "arch_util.h"
#include "vtr_list.h"
#include "vtr_util.h"
#include "vtr_memory.h"
#include "vtr_assert.h"

using vtr::t_linked_vptr;

void PrintArchInfo(FILE* Echo, const t_arch* arch);
static void PrintPb_types_rec(FILE* Echo, const t_pb_type* pb_type, int level);
static void PrintPb_types_recPower(FILE* Echo,
                                   const t_pb_type* pb_type,
                                   const char* tabs);

/* Output the data from architecture data so user can verify it
 * was interpretted correctly. */
void EchoArch(const char* EchoFile,
              const std::vector<t_physical_tile_type>& PhysicalTileTypes,
              const std::vector<t_logical_block_type>& LogicalBlockTypes,
              const t_arch* arch) {
    int i, j;
    FILE* Echo;
    t_model* cur_model;
    t_model_ports* model_port;
    t_linked_vptr* cur_vptr;

    Echo = vtr::fopen(EchoFile, "w");
    cur_model = nullptr;

    //Print all layout device switch/segment list info first
    PrintArchInfo(Echo, arch);

    //Models
    fprintf(Echo, "*************************************************\n");
    for (j = 0; j < 2; j++) {
        if (j == 0) {
            fprintf(Echo, "Printing user models \n");
            cur_model = arch->models;
        } else if (j == 1) {
            fprintf(Echo, "Printing library models \n");
            cur_model = arch->model_library;
        }
        while (cur_model) {
            fprintf(Echo, "Model: \"%s\"\n", cur_model->name);
            model_port = cur_model->inputs;
            while (model_port) {
                fprintf(Echo, "\tInput Ports: \"%s\" \"%d\" min_size=\"%d\"\n",
                        model_port->name, model_port->size,
                        model_port->min_size);
                model_port = model_port->next;
            }
            model_port = cur_model->outputs;
            while (model_port) {
                fprintf(Echo, "\tOutput Ports: \"%s\" \"%d\" min_size=\"%d\"\n",
                        model_port->name, model_port->size,
                        model_port->min_size);
                model_port = model_port->next;
            }
            cur_vptr = cur_model->pb_types;
            i = 0;
            while (cur_vptr != nullptr) {
                fprintf(Echo, "\tpb_type %d: \"%s\"\n", i,
                        ((t_pb_type*)cur_vptr->data_vptr)->name);
                cur_vptr = cur_vptr->next;
                i++;
            }

            cur_model = cur_model->next;
        }
    }
    fprintf(Echo, "*************************************************\n\n");
    fprintf(Echo, "*************************************************\n");
    for (auto& Type : PhysicalTileTypes) {
        fprintf(Echo, "Type: \"%s\"\n", Type.name);
        fprintf(Echo, "\tcapacity: %d\n", Type.capacity);
        fprintf(Echo, "\twidth: %d\n", Type.width);
        fprintf(Echo, "\theight: %d\n", Type.height);
        for (const t_fc_specification& fc_spec : Type.fc_specs) {
            fprintf(Echo, "fc_value_type: ");
            if (fc_spec.fc_value_type == e_fc_value_type::ABSOLUTE) {
                fprintf(Echo, "ABSOLUTE");
            } else if (fc_spec.fc_value_type == e_fc_value_type::FRACTIONAL) {
                fprintf(Echo, "FRACTIONAL");
            } else {
                VTR_ASSERT(false);
            }
            fprintf(Echo, " fc_value: %f", fc_spec.fc_value);
            fprintf(Echo, " segment: %s", arch->Segments[fc_spec.seg_index].name.c_str());
            fprintf(Echo, " pins:");
            for (int pin : fc_spec.pins) {
                fprintf(Echo, " %d", pin);
            }
            fprintf(Echo, "\n");
        }
        fprintf(Echo, "\tnum_drivers: %d\n", Type.num_drivers);
        fprintf(Echo, "\tnum_receivers: %d\n", Type.num_receivers);

        int index = Type.index;
        fprintf(Echo, "\tindex: %d\n", index);

        auto equivalent_sites = get_equivalent_sites_set(&Type);

        for (auto LogicalBlock : equivalent_sites) {
            fprintf(Echo, "\nEquivalent Site: %s\n", LogicalBlock->name);
        }
        fprintf(Echo, "\n");
    }

    fprintf(Echo, "*************************************************\n\n");
    fprintf(Echo, "*************************************************\n");

    for (auto& LogicalBlock : LogicalBlockTypes) {
        if (LogicalBlock.pb_type) {
            PrintPb_types_rec(Echo, LogicalBlock.pb_type, 2);
        }
        fprintf(Echo, "\n");
    }

    fclose(Echo);
}

//Added May 2013 Daniel Chen, help dump arch info after loading from XML
void PrintArchInfo(FILE* Echo, const t_arch* arch) {
    int i, j;

    fprintf(Echo, "Printing architecture... \n\n");
    //Layout
    fprintf(Echo, "*************************************************\n");
    for (const auto& grid_layout : arch->grid_layouts) {
        if (grid_layout.grid_type == GridDefType::AUTO) {
            fprintf(Echo, "Layout: '%s' Type: auto Aspect_Ratio: %f\n", grid_layout.name.c_str(), grid_layout.aspect_ratio);
        } else {
            VTR_ASSERT(grid_layout.grid_type == GridDefType::FIXED);
            fprintf(Echo, "Layout: '%s' Type: fixed Width: %d Height %d\n", grid_layout.name.c_str(), grid_layout.width, grid_layout.height);
        }
    }
    fprintf(Echo, "*************************************************\n\n");
    //Device
    fprintf(Echo, "*************************************************\n");
    fprintf(Echo, "Device Info:\n");

    fprintf(Echo,
            "\tSizing: R_minW_nmos %e R_minW_pmos %e\n",
            arch->R_minW_nmos, arch->R_minW_pmos);

    fprintf(Echo, "\tArea: grid_logic_tile_area %e\n",
            arch->grid_logic_tile_area);

    fprintf(Echo, "\tChannel Width Distribution:\n");

    switch (arch->Chans.chan_x_dist.type) {
        case (UNIFORM):
            fprintf(Echo, "\t\tx: type uniform peak %e\n",
                    arch->Chans.chan_x_dist.peak);
            break;
        case (GAUSSIAN):
            fprintf(Echo,
                    "\t\tx: type gaussian peak %e \
						  width %e Xpeak %e dc %e\n",
                    arch->Chans.chan_x_dist.peak, arch->Chans.chan_x_dist.width,
                    arch->Chans.chan_x_dist.xpeak, arch->Chans.chan_x_dist.dc);
            break;
        case (PULSE):
            fprintf(Echo,
                    "\t\tx: type pulse peak %e \
						  width %e Xpeak %e dc %e\n",
                    arch->Chans.chan_x_dist.peak, arch->Chans.chan_x_dist.width,
                    arch->Chans.chan_x_dist.xpeak, arch->Chans.chan_x_dist.dc);
            break;
        case (DELTA):
            fprintf(Echo,
                    "\t\tx: distr dleta peak %e \
						  Xpeak %e dc %e\n",
                    arch->Chans.chan_x_dist.peak, arch->Chans.chan_x_dist.xpeak,
                    arch->Chans.chan_x_dist.dc);
            break;
        default:
            fprintf(Echo, "\t\tInvalid Distribution!\n");
            break;
    }

    switch (arch->Chans.chan_y_dist.type) {
        case (UNIFORM):
            fprintf(Echo, "\t\ty: type uniform peak %e\n",
                    arch->Chans.chan_y_dist.peak);
            break;
        case (GAUSSIAN):
            fprintf(Echo,
                    "\t\ty: type gaussian peak %e \
						  width %e Xpeak %e dc %e\n",
                    arch->Chans.chan_y_dist.peak, arch->Chans.chan_y_dist.width,
                    arch->Chans.chan_y_dist.xpeak, arch->Chans.chan_y_dist.dc);
            break;
        case (PULSE):
            fprintf(Echo,
                    "\t\ty: type pulse peak %e \
						  width %e Xpeak %e dc %e\n",
                    arch->Chans.chan_y_dist.peak, arch->Chans.chan_y_dist.width,
                    arch->Chans.chan_y_dist.xpeak, arch->Chans.chan_y_dist.dc);
            break;
        case (DELTA):
            fprintf(Echo,
                    "\t\ty: distr dleta peak %e \
						  Xpeak %e dc %e\n",
                    arch->Chans.chan_y_dist.peak, arch->Chans.chan_y_dist.xpeak,
                    arch->Chans.chan_y_dist.dc);
            break;
        default:
            fprintf(Echo, "\t\tInvalid Distribution!\n");
            break;
    }

    switch (arch->SBType) {
        case (WILTON):
            fprintf(Echo, "\tSwitch Block: type wilton fs %d\n", arch->Fs);
            break;
        case (UNIVERSAL):
            fprintf(Echo, "\tSwitch Block: type universal fs %d\n", arch->Fs);
            break;
        case (SUBSET):
            fprintf(Echo, "\tSwitch Block: type subset fs %d\n", arch->Fs);
            break;
        default:
            break;
    }

    fprintf(Echo, "\tInput Connect Block Switch Name: %s\n", arch->ipin_cblock_switch_name.c_str());

    fprintf(Echo, "*************************************************\n\n");
    //Switch list
    fprintf(Echo, "*************************************************\n");
    fprintf(Echo, "Switch List:\n");

    //13 is hard coded because format of %e is always 1.123456e+12
    //It always consists of 10 alphanumeric digits, a decimal
    //and a sign
    for (i = 0; i < arch->num_switches; i++) {
        if (arch->Switches[i].type() == SwitchType::MUX) {
            fprintf(Echo, "\tSwitch[%d]: name %s type mux\n", i + 1, arch->Switches[i].name);
        } else if (arch->Switches[i].type() == SwitchType::TRISTATE) {
            fprintf(Echo, "\tSwitch[%d]: name %s type tristate\n", i + 1, arch->Switches[i].name);
        } else if (arch->Switches[i].type() == SwitchType::SHORT) {
            fprintf(Echo, "\tSwitch[%d]: name %s type short\n", i + 1, arch->Switches[i].name);
        } else if (arch->Switches[i].type() == SwitchType::BUFFER) {
            fprintf(Echo, "\tSwitch[%d]: name %s type buffer\n", i + 1, arch->Switches[i].name);
        } else {
            VTR_ASSERT(arch->Switches[i].type() == SwitchType::PASS_GATE);
            fprintf(Echo, "\tSwitch[%d]: name %s type pass_gate\n", i + 1, arch->Switches[i].name);
        }
        fprintf(Echo, "\t\t\t\tR %e Cin %e Cout %e\n", arch->Switches[i].R,
                arch->Switches[i].Cin, arch->Switches[i].Cout);
        fprintf(Echo, "\t\t\t\t#Tdel values %d buf_size %e mux_trans_size %e\n",
                (int)arch->Switches[i].Tdel_map_.size(), arch->Switches[i].buf_size,
                arch->Switches[i].mux_trans_size);
        if (arch->Switches[i].power_buffer_type == POWER_BUFFER_TYPE_AUTO) {
            fprintf(Echo, "\t\t\t\tpower_buffer_size auto\n");
        } else {
            fprintf(Echo, "\t\t\t\tpower_buffer_size %e\n",
                    arch->Switches[i].power_buffer_size);
        }
    }

    fprintf(Echo, "*************************************************\n\n");
    //Segment List
    fprintf(Echo, "*************************************************\n");
    fprintf(Echo, "Segment List:\n");
    for (i = 0; i < (int)(arch->Segments).size(); i++) {
        const struct t_segment_inf& seg = arch->Segments[i];
        fprintf(Echo,
                "\tSegment[%d]: frequency %d length %d R_metal %e C_metal %e\n",
                i + 1, seg.frequency, seg.length,
                seg.Rmetal, seg.Cmetal);

        if (seg.directionality == UNI_DIRECTIONAL) {
            //wire_switch == arch_opin_switch
            fprintf(Echo, "\t\t\t\ttype unidir mux_name %s\n",
                    arch->Switches[seg.arch_wire_switch].name);
        } else { //Should be bidir
            fprintf(Echo, "\t\t\t\ttype bidir wire_switch %s arch_opin_switch %s\n",
                    arch->Switches[seg.arch_wire_switch].name,
                    arch->Switches[seg.arch_opin_switch].name);
        }

        fprintf(Echo, "\t\t\t\tcb ");
        for (j = 0; j < (int)seg.cb.size(); j++) {
            if (seg.cb[j]) {
                fprintf(Echo, "1 ");
            } else {
                fprintf(Echo, "0 ");
            }
        }
        fprintf(Echo, "\n");

        fprintf(Echo, "\t\t\t\tsb ");
        for (j = 0; j < (int)seg.sb.size(); j++) {
            if (seg.sb[j]) {
                fprintf(Echo, "1 ");
            } else {
                fprintf(Echo, "0 ");
            }
        }
        fprintf(Echo, "\n");
    }
    fprintf(Echo, "*************************************************\n\n");
    //Direct List
    fprintf(Echo, "*************************************************\n");
    fprintf(Echo, "Direct List:\n");
    for (i = 0; i < arch->num_directs; i++) {
        fprintf(Echo, "\tDirect[%d]: name %s from_pin %s to_pin %s\n", i + 1,
                arch->Directs[i].name, arch->Directs[i].from_pin,
                arch->Directs[i].to_pin);
        fprintf(Echo, "\t\t\t\t x_offset %d y_offset %d z_offset %d\n",
                arch->Directs[i].x_offset, arch->Directs[i].y_offset,
                arch->Directs[i].sub_tile_offset);
    }
    fprintf(Echo, "*************************************************\n\n");

    //Architecture Power
    fprintf(Echo, "*************************************************\n");
    fprintf(Echo, "Power:\n");
    if (arch->power) {
        fprintf(Echo, "\tlocal_interconnect C_wire %e factor %f\n",
                arch->power->C_wire_local, arch->power->local_interc_factor);
        fprintf(Echo, "\tlogical_effort_factor %f trans_per_sram_bit %f\n",
                arch->power->logical_effort_factor,
                arch->power->transistors_per_SRAM_bit);
    }

    fprintf(Echo, "*************************************************\n\n");
    //Architecture Clock
    fprintf(Echo, "*************************************************\n");
    fprintf(Echo, "Clock:\n");
    if (arch->clocks) {
        for (i = 0; i < arch->clocks->num_global_clocks; i++) {
            if (arch->clocks->clock_inf[i].autosize_buffer) {
                fprintf(Echo, "\tClock[%d]: buffer_size auto C_wire %e", i + 1,
                        arch->clocks->clock_inf->C_wire);
            } else {
                fprintf(Echo, "\tClock[%d]: buffer_size %e C_wire %e", i + 1,
                        arch->clocks->clock_inf[i].buffer_size,
                        arch->clocks->clock_inf[i].C_wire);
            }
            fprintf(Echo, "\t\t\t\tstat_prob %f switch_density %f period %e",
                    arch->clocks->clock_inf[i].prob,
                    arch->clocks->clock_inf[i].dens,
                    arch->clocks->clock_inf[i].period);
        }
    }

    fprintf(Echo, "*************************************************\n\n");
}

static void PrintPb_types_rec(FILE* Echo, const t_pb_type* pb_type, int level) {
    int i, j, k;
    char* tabs;

    tabs = (char*)vtr::malloc((level + 1) * sizeof(char));
    for (i = 0; i < level; i++) {
        tabs[i] = '\t';
    }
    tabs[level] = '\0';

    fprintf(Echo, "%spb_type name: %s\n", tabs, pb_type->name);
    fprintf(Echo, "%s\tblif_model: %s\n", tabs, pb_type->blif_model);
    fprintf(Echo, "%s\tclass_type: %d\n", tabs, pb_type->class_type);
    fprintf(Echo, "%s\tnum_modes: %d\n", tabs, pb_type->num_modes);
    fprintf(Echo, "%s\tnum_ports: %d\n", tabs, pb_type->num_ports);
    for (i = 0; i < pb_type->num_ports; i++) {
        fprintf(Echo, "%s\tport %s type %d num_pins %d\n", tabs,
                pb_type->ports[i].name, pb_type->ports[i].type,
                pb_type->ports[i].num_pins);
    }

    if (pb_type->num_modes > 0) { /*one or more modes*/
        for (i = 0; i < pb_type->num_modes; i++) {
            fprintf(Echo, "%s\tmode %s:\n", tabs, pb_type->modes[i].name);
            for (j = 0; j < pb_type->modes[i].num_pb_type_children; j++) {
                PrintPb_types_rec(Echo, &pb_type->modes[i].pb_type_children[j],
                                  level + 2);
            }
            for (j = 0; j < pb_type->modes[i].num_interconnect; j++) {
                fprintf(Echo, "%s\t\tinterconnect %d %s %s\n", tabs,
                        pb_type->modes[i].interconnect[j].type,
                        pb_type->modes[i].interconnect[j].input_string,
                        pb_type->modes[i].interconnect[j].output_string);
                for (k = 0;
                     k < pb_type->modes[i].interconnect[j].num_annotations;
                     k++) {
                    fprintf(Echo, "%s\t\t\tannotation %s %s %d: %s\n", tabs,
                            pb_type->modes[i].interconnect[j].annotations[k].input_pins,
                            pb_type->modes[i].interconnect[j].annotations[k].output_pins,
                            pb_type->modes[i].interconnect[j].annotations[k].format,
                            pb_type->modes[i].interconnect[j].annotations[k].value[0]);
                }
                //Print power info for interconnects
                if (pb_type->modes[i].interconnect[j].interconnect_power) {
                    if (pb_type->modes[i].interconnect[j].interconnect_power->power_usage.dynamic
                        || pb_type->modes[i].interconnect[j].interconnect_power->power_usage.leakage) {
                        fprintf(Echo, "%s\t\t\tpower %e %e\n", tabs,
                                pb_type->modes[i].interconnect[j].interconnect_power->power_usage.dynamic,
                                pb_type->modes[i].interconnect[j].interconnect_power->power_usage.leakage);
                    }
                }
            }
        }
    } else { /*leaf pb with unknown model*/
        /*LUT(names) already handled, it naturally has 2 modes.
         * I/O has no annotations to be displayed
         * All other library or user models may have delays specificied, e.g. Tsetup and Tcq
         * Display the additional information*/
        if (strcmp(pb_type->model->name, MODEL_NAMES)
            && strcmp(pb_type->model->name, MODEL_INPUT)
            && strcmp(pb_type->model->name, MODEL_OUTPUT)) {
            for (k = 0; k < pb_type->num_annotations; k++) {
                fprintf(Echo, "%s\t\t\tannotation %s %s %s %d: %s\n", tabs,
                        pb_type->annotations[k].clock,
                        pb_type->annotations[k].input_pins,
                        pb_type->annotations[k].output_pins,
                        pb_type->annotations[k].format,
                        pb_type->annotations[k].value[0]);
            }
        }
    }

    if (pb_type->pb_type_power) {
        PrintPb_types_recPower(Echo, pb_type, tabs);
    }
    free(tabs);
}

//Added May 2013 Daniel Chen, help dump arch info after loading from XML
static void PrintPb_types_recPower(FILE* Echo,
                                   const t_pb_type* pb_type,
                                   const char* tabs) {
    int i = 0;
    /*Print power information for each pb if available*/
    switch (pb_type->pb_type_power->estimation_method) {
        case POWER_METHOD_UNDEFINED:
            fprintf(Echo, "%s\tpower method: undefined\n", tabs);
            break;
        case POWER_METHOD_IGNORE:
            if (pb_type->parent_mode) {
                /*if NOT top-level pb (all top-level pb has NULL parent_mode, check parent's power method
                 * This is because of the inheritance property of auto-size*/
                if (pb_type->parent_mode->parent_pb_type->pb_type_power->estimation_method
                    == POWER_METHOD_IGNORE)
                    break;
            }
            fprintf(Echo, "%s\tpower method: ignore\n", tabs);
            break;
        case POWER_METHOD_SUM_OF_CHILDREN:
            fprintf(Echo, "%s\tpower method: sum-of-children\n", tabs);
            break;
        case POWER_METHOD_AUTO_SIZES:
            if (pb_type->parent_mode) {
                /*if NOT top-level pb (all top-level pb has NULL parent_mode, check parent's power method
                 * This is because of the inheritance property of auto-size*/
                if (pb_type->parent_mode->parent_pb_type->pb_type_power->estimation_method
                    == POWER_METHOD_AUTO_SIZES)
                    break;
            }
            fprintf(Echo, "%s\tpower method: auto-size\n", tabs);
            break;
        case POWER_METHOD_SPECIFY_SIZES:
            if (pb_type->parent_mode) {
                /*if NOT top-level pb (all top-level pb has NULL parent_mode, check parent's power method
                 * This is because of the inheritance property of specify-size*/
                if (pb_type->parent_mode->parent_pb_type->pb_type_power->estimation_method
                    == POWER_METHOD_SPECIFY_SIZES)
                    break;
            }

            fprintf(Echo, "%s\tpower method: specify-size\n", tabs);
            for (i = 0; i < pb_type->num_ports; i++) {
                //Print all the power information on each port, only if available,
                //will not print if value is 0 or NULL
                if (pb_type->ports[i].port_power->buffer_type
                    || pb_type->ports[i].port_power->wire_type
                    || pb_type->pb_type_power->absolute_power_per_instance.leakage
                    || pb_type->pb_type_power->absolute_power_per_instance.dynamic) {
                    fprintf(Echo, "%s\t\tport %s type %d num_pins %d\n", tabs,
                            pb_type->ports[i].name, pb_type->ports[i].type,
                            pb_type->ports[i].num_pins);
                    //Buffer size
                    switch (pb_type->ports[i].port_power->buffer_type) {
                        case (POWER_BUFFER_TYPE_UNDEFINED):
                        case (POWER_BUFFER_TYPE_NONE):
                            break;
                        case (POWER_BUFFER_TYPE_AUTO):
                            fprintf(Echo, "%s\t\t\tbuffer_size %s\n", tabs, "auto");
                            break;
                        case (POWER_BUFFER_TYPE_ABSOLUTE_SIZE):
                            fprintf(Echo, "%s\t\t\tbuffer_size %f\n", tabs,
                                    pb_type->ports[i].port_power->buffer_size);
                            break;
                        default:
                            break;
                    }
                    switch (pb_type->ports[i].port_power->wire_type) {
                        case (POWER_WIRE_TYPE_UNDEFINED):
                        case (POWER_WIRE_TYPE_IGNORED):
                            break;
                        case (POWER_WIRE_TYPE_C):
                            fprintf(Echo, "%s\t\t\twire_cap: %e\n", tabs,
                                    pb_type->ports[i].port_power->wire.C);
                            break;
                        case (POWER_WIRE_TYPE_ABSOLUTE_LENGTH):
                            fprintf(Echo, "%s\t\t\twire_len(abs): %e\n", tabs,
                                    pb_type->ports[i].port_power->wire.absolute_length);
                            break;
                        case (POWER_WIRE_TYPE_RELATIVE_LENGTH):
                            fprintf(Echo, "%s\t\t\twire_len(rel): %f\n", tabs,
                                    pb_type->ports[i].port_power->wire.relative_length);
                            break;
                        case (POWER_WIRE_TYPE_AUTO):
                            fprintf(Echo, "%s\t\t\twire_len: %s\n", tabs, "auto");
                            break;
                        default:
                            break;
                    }
                }
            }
            //Output static power even if non zero
            if (pb_type->pb_type_power->absolute_power_per_instance.leakage)
                fprintf(Echo, "%s\t\tstatic power_per_instance: %e \n", tabs,
                        pb_type->pb_type_power->absolute_power_per_instance.leakage);

            if (pb_type->pb_type_power->absolute_power_per_instance.dynamic)
                fprintf(Echo, "%s\t\tdynamic power_per_instance: %e \n", tabs,
                        pb_type->pb_type_power->absolute_power_per_instance.dynamic);
            break;
        case POWER_METHOD_TOGGLE_PINS:
            if (pb_type->parent_mode) {
                /*if NOT top-level pb (all top-level pb has NULL parent_mode, check parent's power method
                 * This is because once energy_per_toggle is specified at one level,
                 * all children pb's are energy_per_toggle and only want to display once*/
                if (pb_type->parent_mode->parent_pb_type->pb_type_power->estimation_method
                    == POWER_METHOD_TOGGLE_PINS)
                    break;
            }

            fprintf(Echo, "%s\tpower method: pin-toggle\n", tabs);
            for (i = 0; i < pb_type->num_ports; i++) {
                /*Print all the power information on each port, only if available,
                 * will not print if value is 0 or NULL*/
                if (pb_type->ports[i].port_power->energy_per_toggle
                    || pb_type->ports[i].port_power->scaled_by_port
                    || pb_type->pb_type_power->absolute_power_per_instance.leakage
                    || pb_type->pb_type_power->absolute_power_per_instance.dynamic) {
                    fprintf(Echo, "%s\t\tport %s type %d num_pins %d\n", tabs,
                            pb_type->ports[i].name, pb_type->ports[i].type,
                            pb_type->ports[i].num_pins);
                    //Toggle Energy
                    if (pb_type->ports[i].port_power->energy_per_toggle) {
                        fprintf(Echo, "%s\t\t\tenergy_per_toggle %e\n", tabs,
                                pb_type->ports[i].port_power->energy_per_toggle);
                    }
                    //Scaled by port (could be reversed)
                    if (pb_type->ports[i].port_power->scaled_by_port) {
                        if (pb_type->ports[i].port_power->scaled_by_port->num_pins
                            > 1) {
                            fprintf(Echo,
                                    (pb_type->ports[i].port_power->reverse_scaled ? "%s\t\t\tscaled_by_static_prob_n: %s[%d]\n" : "%s\t\t\tscaled_by_static_prob: %s[%d]\n"),
                                    tabs,
                                    pb_type->ports[i].port_power->scaled_by_port->name,
                                    pb_type->ports[i].port_power->scaled_by_port_pin_idx);
                        } else {
                            fprintf(Echo,
                                    (pb_type->ports[i].port_power->reverse_scaled ? "%s\t\t\tscaled_by_static_prob_n: %s\n" : "%s\t\t\tscaled_by_static_prob: %s\n"),
                                    tabs,
                                    pb_type->ports[i].port_power->scaled_by_port->name);
                        }
                    }
                }
            }
            //Output static power even if non zero
            if (pb_type->pb_type_power->absolute_power_per_instance.leakage)
                fprintf(Echo, "%s\t\tstatic power_per_instance: %e \n", tabs,
                        pb_type->pb_type_power->absolute_power_per_instance.leakage);

            if (pb_type->pb_type_power->absolute_power_per_instance.dynamic)
                fprintf(Echo, "%s\t\tdynamic power_per_instance: %e \n", tabs,
                        pb_type->pb_type_power->absolute_power_per_instance.dynamic);

            break;
        case POWER_METHOD_C_INTERNAL:
            if (pb_type->parent_mode) {
                /*if NOT top-level pb (all top-level pb has NULL parent_mode, check parent's power method
                 * This is because of values at this level includes all children pb's*/
                if (pb_type->parent_mode->parent_pb_type->pb_type_power->estimation_method
                    == POWER_METHOD_C_INTERNAL)
                    break;
            }
            fprintf(Echo, "%s\tpower method: C-internal\n", tabs);

            if (pb_type->pb_type_power->absolute_power_per_instance.leakage)
                fprintf(Echo, "%s\t\tstatic power_per_instance: %e \n", tabs,
                        pb_type->pb_type_power->absolute_power_per_instance.leakage);

            if (pb_type->pb_type_power->C_internal)
                fprintf(Echo, "%s\t\tdynamic c-internal: %e \n", tabs,
                        pb_type->pb_type_power->C_internal);
            break;
        case POWER_METHOD_ABSOLUTE:
            if (pb_type->parent_mode) {
                /*if NOT top-level pb (all top-level pb has NULL parent_mode, check parent's power method
                 * This is because of values at this level includes all children pb's*/
                if (pb_type->parent_mode->parent_pb_type->pb_type_power->estimation_method
                    == POWER_METHOD_ABSOLUTE)
                    break;
            }
            fprintf(Echo, "%s\tpower method: absolute\n", tabs);
            if (pb_type->pb_type_power->absolute_power_per_instance.leakage)
                fprintf(Echo, "%s\t\tstatic power_per_instance: %e \n", tabs,
                        pb_type->pb_type_power->absolute_power_per_instance.leakage);

            if (pb_type->pb_type_power->absolute_power_per_instance.dynamic)
                fprintf(Echo, "%s\t\tdynamic power_per_instance: %e \n", tabs,
                        pb_type->pb_type_power->absolute_power_per_instance.dynamic);
            break;
        default:
            fprintf(Echo, "%s\tpower method: error has occcured\n", tabs);
            break;
    }
}
